/*
 * Copyright 2019 Adobe. All rights reserved.
 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. You may obtain a copy
 * of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under
 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
 * OF ANY KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
#include <lagrange/Logger.h>
#include <lagrange/ui/types/GLContext.h>
#include <cassert>

namespace lagrange {
namespace ui {

std::unique_ptr<GLState> GLState::m_instance = nullptr;
int GLState::major_version = 4;
int GLState::minor_version = 1;

std::string GLState::get_glsl_version_string()
{
#if defined(__EMSCRIPTEN__)
    return "#version 300 es";
#else
    std::string res = "#version ";

    std::string v = "";

    if (major_version == 2) {
        if (minor_version == 0) {
            v = "110";
        } else if (minor_version == 1) {
            v = "120";
        }
    } else if (major_version == 3) {
        if (minor_version == 0) {
            v = "130";
        } else if (minor_version == 1) {
            v = "140";
        } else if (minor_version == 2) {
            v = "150";
        } else if (minor_version == 3) {
            v = "330";
        }
    } else if (major_version == 4) {
        if (minor_version == 0) {
            v = "400";
        } else if (minor_version == 1) {
            v = "410";
        } else if (minor_version == 2) {
            v = "420";
        } else if (minor_version == 3) {
            v = "430";
        } else if (minor_version == 4) {
            v = "440";
        }
    }

    return res + v;
#endif
}

bool GLState::on_opengl_thread()
{
    return GLState::get().m_gl_thread_id == std::this_thread::get_id();
}

std::string_view GLState::get_enum_string(GLenum value)
{
    switch (value) {
    case GL_DEPTH_BUFFER_BIT: return "GL_DEPTH_BUFFER_BIT";
    case GL_STENCIL_BUFFER_BIT: return "GL_STENCIL_BUFFER_BIT";
    case GL_COLOR_BUFFER_BIT: return "GL_COLOR_BUFFER_BIT";
    case 0: return "GL_ZERO/FALSE";
    case 1: return "GL_ONE/TRUE";
    case GL_LINE_LOOP: return "GL_LINE_LOOP";
    case GL_LINE_STRIP: return "GL_LINE_STRIP";
    case GL_TRIANGLES: return "GL_TRIANGLES";
    case GL_TRIANGLE_STRIP: return "GL_TRIANGLE_STRIP";
    case GL_TRIANGLE_FAN: return "GL_TRIANGLE_FAN";
    case GL_SRC_COLOR: return "GL_SRC_COLOR";
    case GL_ONE_MINUS_SRC_COLOR: return "GL_ONE_MINUS_SRC_COLOR";
    case GL_SRC_ALPHA: return "GL_SRC_ALPHA";
    case GL_ONE_MINUS_SRC_ALPHA: return "GL_ONE_MINUS_SRC_ALPHA";
    case GL_DST_ALPHA: return "GL_DST_ALPHA";
    case GL_ONE_MINUS_DST_ALPHA: return "GL_ONE_MINUS_DST_ALPHA";
    case GL_DST_COLOR: return "GL_DST_COLOR";
    case GL_ONE_MINUS_DST_COLOR: return "GL_ONE_MINUS_DST_COLOR";
    case GL_SRC_ALPHA_SATURATE: return "GL_SRC_ALPHA_SATURATE";
    case GL_FUNC_ADD: return "GL_FUNC_ADD";
    case GL_BLEND_EQUATION: return "GL_BLEND_EQUATION";
    case GL_BLEND_EQUATION_ALPHA: return "GL_BLEND_EQUATION_ALPHA";
    case GL_FUNC_SUBTRACT: return "GL_FUNC_SUBTRACT";
    case GL_FUNC_REVERSE_SUBTRACT: return "GL_FUNC_REVERSE_SUBTRACT";
    case GL_BLEND_DST_RGB: return "GL_BLEND_DST_RGB";
    case GL_BLEND_SRC_RGB: return "GL_BLEND_SRC_RGB";
    case GL_BLEND_DST_ALPHA: return "GL_BLEND_DST_ALPHA";
    case GL_BLEND_SRC_ALPHA: return "GL_BLEND_SRC_ALPHA";
    case GL_CONSTANT_COLOR: return "GL_CONSTANT_COLOR";
    case GL_ONE_MINUS_CONSTANT_COLOR: return "GL_ONE_MINUS_CONSTANT_COLOR";
    case GL_CONSTANT_ALPHA: return "GL_CONSTANT_ALPHA";
    case GL_ONE_MINUS_CONSTANT_ALPHA: return "GL_ONE_MINUS_CONSTANT_ALPHA";
    case GL_BLEND_COLOR: return "GL_BLEND_COLOR";
    case GL_ARRAY_BUFFER: return "GL_ARRAY_BUFFER";
    case GL_ELEMENT_ARRAY_BUFFER: return "GL_ELEMENT_ARRAY_BUFFER";
    case GL_ARRAY_BUFFER_BINDING: return "GL_ARRAY_BUFFER_BINDING";
    case GL_ELEMENT_ARRAY_BUFFER_BINDING: return "GL_ELEMENT_ARRAY_BUFFER_BINDING";
    case GL_STREAM_DRAW: return "GL_STREAM_DRAW";
    case GL_STATIC_DRAW: return "GL_STATIC_DRAW";
    case GL_DYNAMIC_DRAW: return "GL_DYNAMIC_DRAW";
    case GL_BUFFER_SIZE: return "GL_BUFFER_SIZE";
    case GL_BUFFER_USAGE: return "GL_BUFFER_USAGE";
    case GL_CURRENT_VERTEX_ATTRIB: return "GL_CURRENT_VERTEX_ATTRIB";
    case GL_FRONT: return "GL_FRONT";
    case GL_BACK: return "GL_BACK";
    case GL_FRONT_AND_BACK: return "GL_FRONT_AND_BACK";
    case GL_TEXTURE_2D: return "GL_TEXTURE_2D";
    case GL_CULL_FACE: return "GL_CULL_FACE";
    case GL_BLEND: return "GL_BLEND";
    case GL_DITHER: return "GL_DITHER";
    case GL_STENCIL_TEST: return "GL_STENCIL_TEST";
    case GL_DEPTH_TEST: return "GL_DEPTH_TEST";
    case GL_SCISSOR_TEST: return "GL_SCISSOR_TEST";
    case GL_POLYGON_OFFSET_FILL: return "GL_POLYGON_OFFSET_FILL";
    case GL_SAMPLE_ALPHA_TO_COVERAGE: return "GL_SAMPLE_ALPHA_TO_COVERAGE";
    case GL_SAMPLE_COVERAGE: return "GL_SAMPLE_COVERAGE";
    case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
    case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
    case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
    case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
    case GL_CW: return "GL_CW";
    case GL_CCW: return "GL_CCW";
    case GL_LINE_WIDTH: return "GL_LINE_WIDTH";
#ifdef GL_ALIASED_POINT_SIZE_RANGE
    case GL_ALIASED_POINT_SIZE_RANGE: return "GL_ALIASED_POINT_SIZE_RANGE";
#endif
    case GL_ALIASED_LINE_WIDTH_RANGE: return "GL_ALIASED_LINE_WIDTH_RANGE";
    case GL_CULL_FACE_MODE: return "GL_CULL_FACE_MODE";
    case GL_FRONT_FACE: return "GL_FRONT_FACE";
    case GL_DEPTH_RANGE: return "GL_DEPTH_RANGE";
    case GL_DEPTH_WRITEMASK: return "GL_DEPTH_WRITEMASK";
    case GL_DEPTH_CLEAR_VALUE: return "GL_DEPTH_CLEAR_VALUE";
    case GL_DEPTH_FUNC: return "GL_DEPTH_FUNC";
    case GL_STENCIL_CLEAR_VALUE: return "GL_STENCIL_CLEAR_VALUE";
    case GL_STENCIL_FUNC: return "GL_STENCIL_FUNC";
    case GL_STENCIL_FAIL: return "GL_STENCIL_FAIL";
    case GL_STENCIL_PASS_DEPTH_FAIL: return "GL_STENCIL_PASS_DEPTH_FAIL";
    case GL_STENCIL_PASS_DEPTH_PASS: return "GL_STENCIL_PASS_DEPTH_PASS";
    case GL_STENCIL_REF: return "GL_STENCIL_REF";
    case GL_STENCIL_VALUE_MASK: return "GL_STENCIL_VALUE_MASK";
    case GL_STENCIL_WRITEMASK: return "GL_STENCIL_WRITEMASK";
    case GL_STENCIL_BACK_FUNC: return "GL_STENCIL_BACK_FUNC";
    case GL_STENCIL_BACK_FAIL: return "GL_STENCIL_BACK_FAIL";
    case GL_STENCIL_BACK_PASS_DEPTH_FAIL: return "GL_STENCIL_BACK_PASS_DEPTH_FAIL";
    case GL_STENCIL_BACK_PASS_DEPTH_PASS: return "GL_STENCIL_BACK_PASS_DEPTH_PASS";
    case GL_STENCIL_BACK_REF: return "GL_STENCIL_BACK_REF";
    case GL_STENCIL_BACK_VALUE_MASK: return "GL_STENCIL_BACK_VALUE_MASK";
    case GL_STENCIL_BACK_WRITEMASK: return "GL_STENCIL_BACK_WRITEMASK";
    case GL_VIEWPORT: return "GL_VIEWPORT";
    case GL_SCISSOR_BOX: return "GL_SCISSOR_BOX";
    case GL_COLOR_CLEAR_VALUE: return "GL_COLOR_CLEAR_VALUE";
    case GL_COLOR_WRITEMASK: return "GL_COLOR_WRITEMASK";
    case GL_UNPACK_ALIGNMENT: return "GL_UNPACK_ALIGNMENT";
    case GL_PACK_ALIGNMENT: return "GL_PACK_ALIGNMENT";
    case GL_MAX_TEXTURE_SIZE: return "GL_MAX_TEXTURE_SIZE";
    case GL_MAX_VIEWPORT_DIMS: return "GL_MAX_VIEWPORT_DIMS";
    case GL_SUBPIXEL_BITS: return "GL_SUBPIXEL_BITS";
#ifdef GL_RED_BITS
    case GL_RED_BITS: return "GL_RED_BITS";
    case GL_GREEN_BITS: return "GL_GREEN_BITS";
    case GL_BLUE_BITS: return "GL_BLUE_BITS";
    case GL_ALPHA_BITS: return "GL_ALPHA_BITS";
    case GL_DEPTH_BITS: return "GL_DEPTH_BITS";
    case GL_STENCIL_BITS: return "GL_STENCIL_BITS";
#endif
    case GL_POLYGON_OFFSET_UNITS: return "GL_POLYGON_OFFSET_UNITS";
    case GL_POLYGON_OFFSET_FACTOR: return "GL_POLYGON_OFFSET_FACTOR";
    case GL_TEXTURE_BINDING_2D: return "GL_TEXTURE_BINDING_2D";
    case GL_SAMPLE_BUFFERS: return "GL_SAMPLE_BUFFERS";
    case GL_SAMPLES: return "GL_SAMPLES";
    case GL_SAMPLE_COVERAGE_VALUE: return "GL_SAMPLE_COVERAGE_VALUE";
    case GL_SAMPLE_COVERAGE_INVERT: return "GL_SAMPLE_COVERAGE_INVERT";
    case GL_NUM_COMPRESSED_TEXTURE_FORMATS: return "GL_NUM_COMPRESSED_TEXTURE_FORMATS";
    case GL_COMPRESSED_TEXTURE_FORMATS: return "GL_COMPRESSED_TEXTURE_FORMATS";
    case GL_DONT_CARE: return "GL_DONT_CARE";
    case GL_FASTEST: return "GL_FASTEST";
    case GL_NICEST: return "GL_NICEST";
#ifdef GL_GENERATE_MIPMAP_HINT
    case GL_GENERATE_MIPMAP_HINT: return "GL_GENERATE_MIPMAP_HINT";
#endif
    case GL_BYTE: return "GL_BYTE";
    case GL_UNSIGNED_BYTE: return "GL_UNSIGNED_BYTE";
    case GL_SHORT: return "GL_SHORT";
    case GL_UNSIGNED_SHORT: return "GL_UNSIGNED_SHORT";
    case GL_INT: return "GL_INT";
    case GL_UNSIGNED_INT: return "GL_UNSIGNED_INT";
    case GL_FLOAT: return "GL_FLOAT";
    case GL_FIXED: return "GL_FIXED";
    case GL_DEPTH_COMPONENT: return "GL_DEPTH_COMPONENT";
    case GL_ALPHA: return "GL_ALPHA";
    case GL_RGB: return "GL_RGB";
    case GL_RGBA: return "GL_RGBA";
#ifdef GL_LUMINANCE
    case GL_LUMINANCE: return "GL_LUMINANCE";
    case GL_LUMINANCE_ALPHA: return "GL_LUMINANCE_ALPHA";
#endif
    case GL_UNSIGNED_SHORT_4_4_4_4: return "GL_UNSIGNED_SHORT_4_4_4_4";
    case GL_UNSIGNED_SHORT_5_5_5_1: return "GL_UNSIGNED_SHORT_5_5_5_1";
    case GL_UNSIGNED_SHORT_5_6_5: return "GL_UNSIGNED_SHORT_5_6_5";
    case GL_FRAGMENT_SHADER: return "GL_FRAGMENT_SHADER";
    case GL_VERTEX_SHADER: return "GL_VERTEX_SHADER";
    case GL_MAX_VERTEX_ATTRIBS: return "GL_MAX_VERTEX_ATTRIBS";
    case GL_MAX_VERTEX_UNIFORM_VECTORS: return "GL_MAX_VERTEX_UNIFORM_VECTORS";
    case GL_MAX_VARYING_VECTORS: return "GL_MAX_VARYING_VECTORS";
    case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: return "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS";
    case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: return "GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS";
    case GL_MAX_TEXTURE_IMAGE_UNITS: return "GL_MAX_TEXTURE_IMAGE_UNITS";
    case GL_MAX_FRAGMENT_UNIFORM_VECTORS: return "GL_MAX_FRAGMENT_UNIFORM_VECTORS";
    case GL_SHADER_TYPE: return "GL_SHADER_TYPE";
    case GL_DELETE_STATUS: return "GL_DELETE_STATUS";
    case GL_LINK_STATUS: return "GL_LINK_STATUS";
    case GL_VALIDATE_STATUS: return "GL_VALIDATE_STATUS";
    case GL_ATTACHED_SHADERS: return "GL_ATTACHED_SHADERS";
    case GL_ACTIVE_UNIFORMS: return "GL_ACTIVE_UNIFORMS";
    case GL_ACTIVE_UNIFORM_MAX_LENGTH: return "GL_ACTIVE_UNIFORM_MAX_LENGTH";
    case GL_ACTIVE_ATTRIBUTES: return "GL_ACTIVE_ATTRIBUTES";
    case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: return "GL_ACTIVE_ATTRIBUTE_MAX_LENGTH";
    case GL_SHADING_LANGUAGE_VERSION: return "GL_SHADING_LANGUAGE_VERSION";
    case GL_CURRENT_PROGRAM: return "GL_CURRENT_PROGRAM";
    case GL_NEVER: return "GL_NEVER";
    case GL_LESS: return "GL_LESS";
    case GL_EQUAL: return "GL_EQUAL";
    case GL_LEQUAL: return "GL_LEQUAL";
    case GL_GREATER: return "GL_GREATER";
    case GL_NOTEQUAL: return "GL_NOTEQUAL";
    case GL_GEQUAL: return "GL_GEQUAL";
    case GL_ALWAYS: return "GL_ALWAYS";
    case GL_KEEP: return "GL_KEEP";
    case GL_REPLACE: return "GL_REPLACE";
    case GL_INCR: return "GL_INCR";
    case GL_DECR: return "GL_DECR";
    case GL_INVERT: return "GL_INVERT";
    case GL_INCR_WRAP: return "GL_INCR_WRAP";
    case GL_DECR_WRAP: return "GL_DECR_WRAP";
    case GL_VENDOR: return "GL_VENDOR";
    case GL_RENDERER: return "GL_RENDERER";
    case GL_VERSION: return "GL_VERSION";
    case GL_EXTENSIONS: return "GL_EXTENSIONS";
    case GL_NEAREST: return "GL_NEAREST";
    case GL_LINEAR: return "GL_LINEAR";
    case GL_NEAREST_MIPMAP_NEAREST: return "GL_NEAREST_MIPMAP_NEAREST";
    case GL_LINEAR_MIPMAP_NEAREST: return "GL_LINEAR_MIPMAP_NEAREST";
    case GL_NEAREST_MIPMAP_LINEAR: return "GL_NEAREST_MIPMAP_LINEAR";
    case GL_LINEAR_MIPMAP_LINEAR: return "GL_LINEAR_MIPMAP_LINEAR";
    case GL_TEXTURE_MAG_FILTER: return "GL_TEXTURE_MAG_FILTER";
    case GL_TEXTURE_MIN_FILTER: return "GL_TEXTURE_MIN_FILTER";
    case GL_TEXTURE_WRAP_S: return "GL_TEXTURE_WRAP_S";
    case GL_TEXTURE_WRAP_T: return "GL_TEXTURE_WRAP_T";
    case GL_TEXTURE: return "GL_TEXTURE";
    case GL_TEXTURE_CUBE_MAP: return "GL_TEXTURE_CUBE_MAP";
    case GL_TEXTURE_BINDING_CUBE_MAP: return "GL_TEXTURE_BINDING_CUBE_MAP";
    case GL_TEXTURE_CUBE_MAP_POSITIVE_X: return "GL_TEXTURE_CUBE_MAP_POSITIVE_X";
    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: return "GL_TEXTURE_CUBE_MAP_NEGATIVE_X";
    case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: return "GL_TEXTURE_CUBE_MAP_POSITIVE_Y";
    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: return "GL_TEXTURE_CUBE_MAP_NEGATIVE_Y";
    case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: return "GL_TEXTURE_CUBE_MAP_POSITIVE_Z";
    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return "GL_TEXTURE_CUBE_MAP_NEGATIVE_Z";
    case GL_MAX_CUBE_MAP_TEXTURE_SIZE: return "GL_MAX_CUBE_MAP_TEXTURE_SIZE";
    case GL_TEXTURE0: return "GL_TEXTURE0";
    case GL_TEXTURE1: return "GL_TEXTURE1";
    case GL_TEXTURE2: return "GL_TEXTURE2";
    case GL_TEXTURE3: return "GL_TEXTURE3";
    case GL_TEXTURE4: return "GL_TEXTURE4";
    case GL_TEXTURE5: return "GL_TEXTURE5";
    case GL_TEXTURE6: return "GL_TEXTURE6";
    case GL_TEXTURE7: return "GL_TEXTURE7";
    case GL_TEXTURE8: return "GL_TEXTURE8";
    case GL_TEXTURE9: return "GL_TEXTURE9";
    case GL_TEXTURE10: return "GL_TEXTURE10";
    case GL_TEXTURE11: return "GL_TEXTURE11";
    case GL_TEXTURE12: return "GL_TEXTURE12";
    case GL_TEXTURE13: return "GL_TEXTURE13";
    case GL_TEXTURE14: return "GL_TEXTURE14";
    case GL_TEXTURE15: return "GL_TEXTURE15";
    case GL_TEXTURE16: return "GL_TEXTURE16";
    case GL_TEXTURE17: return "GL_TEXTURE17";
    case GL_TEXTURE18: return "GL_TEXTURE18";
    case GL_TEXTURE19: return "GL_TEXTURE19";
    case GL_TEXTURE20: return "GL_TEXTURE20";
    case GL_TEXTURE21: return "GL_TEXTURE21";
    case GL_TEXTURE22: return "GL_TEXTURE22";
    case GL_TEXTURE23: return "GL_TEXTURE23";
    case GL_TEXTURE24: return "GL_TEXTURE24";
    case GL_TEXTURE25: return "GL_TEXTURE25";
    case GL_TEXTURE26: return "GL_TEXTURE26";
    case GL_TEXTURE27: return "GL_TEXTURE27";
    case GL_TEXTURE28: return "GL_TEXTURE28";
    case GL_TEXTURE29: return "GL_TEXTURE29";
    case GL_TEXTURE30: return "GL_TEXTURE30";
    case GL_TEXTURE31: return "GL_TEXTURE31";
    case GL_ACTIVE_TEXTURE: return "GL_ACTIVE_TEXTURE";
    case GL_REPEAT: return "GL_REPEAT";
    case GL_CLAMP_TO_EDGE: return "GL_CLAMP_TO_EDGE";
    case GL_MIRRORED_REPEAT: return "GL_MIRRORED_REPEAT";
    case GL_FLOAT_VEC2: return "GL_FLOAT_VEC2";
    case GL_FLOAT_VEC3: return "GL_FLOAT_VEC3";
    case GL_FLOAT_VEC4: return "GL_FLOAT_VEC4";
    case GL_INT_VEC2: return "GL_INT_VEC2";
    case GL_INT_VEC3: return "GL_INT_VEC3";
    case GL_INT_VEC4: return "GL_INT_VEC4";
    case GL_BOOL: return "GL_BOOL";
    case GL_BOOL_VEC2: return "GL_BOOL_VEC2";
    case GL_BOOL_VEC3: return "GL_BOOL_VEC3";
    case GL_BOOL_VEC4: return "GL_BOOL_VEC4";
    case GL_FLOAT_MAT2: return "GL_FLOAT_MAT2";
    case GL_FLOAT_MAT3: return "GL_FLOAT_MAT3";
    case GL_FLOAT_MAT4: return "GL_FLOAT_MAT4";
    case GL_SAMPLER_2D: return "GL_SAMPLER_2D";
    case GL_SAMPLER_CUBE: return "GL_SAMPLER_CUBE";
    case GL_VERTEX_ATTRIB_ARRAY_ENABLED: return "GL_VERTEX_ATTRIB_ARRAY_ENABLED";
    case GL_VERTEX_ATTRIB_ARRAY_SIZE: return "GL_VERTEX_ATTRIB_ARRAY_SIZE";
    case GL_VERTEX_ATTRIB_ARRAY_STRIDE: return "GL_VERTEX_ATTRIB_ARRAY_STRIDE";
    case GL_VERTEX_ATTRIB_ARRAY_TYPE: return "GL_VERTEX_ATTRIB_ARRAY_TYPE";
    case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: return "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED";
    case GL_VERTEX_ATTRIB_ARRAY_POINTER: return "GL_VERTEX_ATTRIB_ARRAY_POINTER";
    case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: return "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING";
    case GL_IMPLEMENTATION_COLOR_READ_TYPE: return "GL_IMPLEMENTATION_COLOR_READ_TYPE";
    case GL_IMPLEMENTATION_COLOR_READ_FORMAT: return "GL_IMPLEMENTATION_COLOR_READ_FORMAT";
    case GL_COMPILE_STATUS: return "GL_COMPILE_STATUS";
    case GL_INFO_LOG_LENGTH: return "GL_INFO_LOG_LENGTH";
    case GL_SHADER_SOURCE_LENGTH: return "GL_SHADER_SOURCE_LENGTH";
    case GL_SHADER_COMPILER: return "GL_SHADER_COMPILER";
    case GL_SHADER_BINARY_FORMATS: return "GL_SHADER_BINARY_FORMATS";
    case GL_NUM_SHADER_BINARY_FORMATS: return "GL_NUM_SHADER_BINARY_FORMATS";
    case GL_LOW_FLOAT: return "GL_LOW_FLOAT";
    case GL_MEDIUM_FLOAT: return "GL_MEDIUM_FLOAT";
    case GL_HIGH_FLOAT: return "GL_HIGH_FLOAT";
    case GL_LOW_INT: return "GL_LOW_INT";
    case GL_MEDIUM_INT: return "GL_MEDIUM_INT";
    case GL_HIGH_INT: return "GL_HIGH_INT";
    case GL_FRAMEBUFFER: return "GL_FRAMEBUFFER";
    case GL_RENDERBUFFER: return "GL_RENDERBUFFER";
    case GL_RGBA4: return "GL_RGBA4";
    case GL_RGB5_A1: return "GL_RGB5_A1";
    case GL_RGB565: return "GL_RGB565";
    case GL_DEPTH_COMPONENT16: return "GL_DEPTH_COMPONENT16";
    case GL_STENCIL_INDEX8: return "GL_STENCIL_INDEX8";
    case GL_RENDERBUFFER_WIDTH: return "GL_RENDERBUFFER_WIDTH";
    case GL_RENDERBUFFER_HEIGHT: return "GL_RENDERBUFFER_HEIGHT";
    case GL_RENDERBUFFER_INTERNAL_FORMAT: return "GL_RENDERBUFFER_INTERNAL_FORMAT";
    case GL_RENDERBUFFER_RED_SIZE: return "GL_RENDERBUFFER_RED_SIZE";
    case GL_RENDERBUFFER_GREEN_SIZE: return "GL_RENDERBUFFER_GREEN_SIZE";
    case GL_RENDERBUFFER_BLUE_SIZE: return "GL_RENDERBUFFER_BLUE_SIZE";
    case GL_RENDERBUFFER_ALPHA_SIZE: return "GL_RENDERBUFFER_ALPHA_SIZE";
    case GL_RENDERBUFFER_DEPTH_SIZE: return "GL_RENDERBUFFER_DEPTH_SIZE";
    case GL_RENDERBUFFER_STENCIL_SIZE: return "GL_RENDERBUFFER_STENCIL_SIZE";
    case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: return "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE";
    case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: return "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME";
    case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: return "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL";
    case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
        return "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE";
    case GL_COLOR_ATTACHMENT0: return "GL_COLOR_ATTACHMENT0";
    case GL_DEPTH_ATTACHMENT: return "GL_DEPTH_ATTACHMENT";
    case GL_STENCIL_ATTACHMENT: return "GL_STENCIL_ATTACHMENT";

    case GL_FRAMEBUFFER_COMPLETE: return "GL_FRAMEBUFFER_COMPLETE";
    case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
    case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
        return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";

#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
    case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
#endif
    case GL_FRAMEBUFFER_UNSUPPORTED: return "GL_FRAMEBUFFER_UNSUPPORTED";
    case GL_FRAMEBUFFER_BINDING: return "GL_FRAMEBUFFER_BINDING";
    case GL_RENDERBUFFER_BINDING: return "GL_RENDERBUFFER_BINDING";
    case GL_MAX_RENDERBUFFER_SIZE: return "GL_MAX_RENDERBUFFER_SIZE";
    case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
    case GL_READ_BUFFER: return "GL_READ_BUFFER";
    case GL_UNPACK_ROW_LENGTH: return "GL_UNPACK_ROW_LENGTH";
    case GL_UNPACK_SKIP_ROWS: return "GL_UNPACK_SKIP_ROWS";
    case GL_UNPACK_SKIP_PIXELS: return "GL_UNPACK_SKIP_PIXELS";
    case GL_PACK_ROW_LENGTH: return "GL_PACK_ROW_LENGTH";
    case GL_PACK_SKIP_ROWS: return "GL_PACK_SKIP_ROWS";
    case GL_PACK_SKIP_PIXELS: return "GL_PACK_SKIP_PIXELS";
    case GL_COLOR: return "GL_COLOR";
    case GL_DEPTH: return "GL_DEPTH";
    case GL_STENCIL: return "GL_STENCIL";
    case GL_RED: return "GL_RED";
    case GL_RGB8: return "GL_RGB8";
    case GL_RGBA8: return "GL_RGBA8";
    case GL_RGB10_A2: return "GL_RGB10_A2";
    case GL_TEXTURE_BINDING_3D: return "GL_TEXTURE_BINDING_3D";
    case GL_UNPACK_SKIP_IMAGES: return "GL_UNPACK_SKIP_IMAGES";
    case GL_UNPACK_IMAGE_HEIGHT: return "GL_UNPACK_IMAGE_HEIGHT";
    case GL_TEXTURE_3D: return "GL_TEXTURE_3D";
    case GL_TEXTURE_WRAP_R: return "GL_TEXTURE_WRAP_R";
    case GL_MAX_3D_TEXTURE_SIZE: return "GL_MAX_3D_TEXTURE_SIZE";
    case GL_UNSIGNED_INT_2_10_10_10_REV: return "GL_UNSIGNED_INT_2_10_10_10_REV";
    case GL_MAX_ELEMENTS_VERTICES: return "GL_MAX_ELEMENTS_VERTICES";
    case GL_MAX_ELEMENTS_INDICES: return "GL_MAX_ELEMENTS_INDICES";
    case GL_TEXTURE_MIN_LOD: return "GL_TEXTURE_MIN_LOD";
    case GL_TEXTURE_MAX_LOD: return "GL_TEXTURE_MAX_LOD";
    case GL_TEXTURE_BASE_LEVEL: return "GL_TEXTURE_BASE_LEVEL";
    case GL_TEXTURE_MAX_LEVEL: return "GL_TEXTURE_MAX_LEVEL";
    case GL_MIN: return "GL_MIN";
    case GL_MAX: return "GL_MAX";
    case GL_DEPTH_COMPONENT24: return "GL_DEPTH_COMPONENT24";
    case GL_MAX_TEXTURE_LOD_BIAS: return "GL_MAX_TEXTURE_LOD_BIAS";
    case GL_TEXTURE_COMPARE_MODE: return "GL_TEXTURE_COMPARE_MODE";
    case GL_TEXTURE_COMPARE_FUNC: return "GL_TEXTURE_COMPARE_FUNC";
    case GL_CURRENT_QUERY: return "GL_CURRENT_QUERY";
    case GL_QUERY_RESULT: return "GL_QUERY_RESULT";
    case GL_QUERY_RESULT_AVAILABLE: return "GL_QUERY_RESULT_AVAILABLE";
    case GL_BUFFER_MAPPED: return "GL_BUFFER_MAPPED";
    case GL_BUFFER_MAP_POINTER: return "GL_BUFFER_MAP_POINTER";
    case GL_STREAM_READ: return "GL_STREAM_READ";
    case GL_STREAM_COPY: return "GL_STREAM_COPY";
    case GL_STATIC_READ: return "GL_STATIC_READ";
    case GL_STATIC_COPY: return "GL_STATIC_COPY";
    case GL_DYNAMIC_READ: return "GL_DYNAMIC_READ";
    case GL_DYNAMIC_COPY: return "GL_DYNAMIC_COPY";
    case GL_MAX_DRAW_BUFFERS: return "GL_MAX_DRAW_BUFFERS";
    case GL_DRAW_BUFFER0: return "GL_DRAW_BUFFER0";
    case GL_DRAW_BUFFER1: return "GL_DRAW_BUFFER1";
    case GL_DRAW_BUFFER2: return "GL_DRAW_BUFFER2";
    case GL_DRAW_BUFFER3: return "GL_DRAW_BUFFER3";
    case GL_DRAW_BUFFER4: return "GL_DRAW_BUFFER4";
    case GL_DRAW_BUFFER5: return "GL_DRAW_BUFFER5";
    case GL_DRAW_BUFFER6: return "GL_DRAW_BUFFER6";
    case GL_DRAW_BUFFER7: return "GL_DRAW_BUFFER7";
    case GL_DRAW_BUFFER8: return "GL_DRAW_BUFFER8";
    case GL_DRAW_BUFFER9: return "GL_DRAW_BUFFER9";
    case GL_DRAW_BUFFER10: return "GL_DRAW_BUFFER10";
    case GL_DRAW_BUFFER11: return "GL_DRAW_BUFFER11";
    case GL_DRAW_BUFFER12: return "GL_DRAW_BUFFER12";
    case GL_DRAW_BUFFER13: return "GL_DRAW_BUFFER13";
    case GL_DRAW_BUFFER14: return "GL_DRAW_BUFFER14";
    case GL_DRAW_BUFFER15: return "GL_DRAW_BUFFER15";
    case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: return "GL_MAX_FRAGMENT_UNIFORM_COMPONENTS";
    case GL_MAX_VERTEX_UNIFORM_COMPONENTS: return "GL_MAX_VERTEX_UNIFORM_COMPONENTS";
    case GL_SAMPLER_3D: return "GL_SAMPLER_3D";
    case GL_SAMPLER_2D_SHADOW: return "GL_SAMPLER_2D_SHADOW";
    case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: return "GL_FRAGMENT_SHADER_DERIVATIVE_HINT";
    case GL_PIXEL_PACK_BUFFER: return "GL_PIXEL_PACK_BUFFER";
    case GL_PIXEL_UNPACK_BUFFER: return "GL_PIXEL_UNPACK_BUFFER";
    case GL_PIXEL_PACK_BUFFER_BINDING: return "GL_PIXEL_PACK_BUFFER_BINDING";
    case GL_PIXEL_UNPACK_BUFFER_BINDING: return "GL_PIXEL_UNPACK_BUFFER_BINDING";
    case GL_FLOAT_MAT2x3: return "GL_FLOAT_MAT2x3";
    case GL_FLOAT_MAT2x4: return "GL_FLOAT_MAT2x4";
    case GL_FLOAT_MAT3x2: return "GL_FLOAT_MAT3x2";
    case GL_FLOAT_MAT3x4: return "GL_FLOAT_MAT3x4";
    case GL_FLOAT_MAT4x2: return "GL_FLOAT_MAT4x2";
    case GL_FLOAT_MAT4x3: return "GL_FLOAT_MAT4x3";
    case GL_SRGB: return "GL_SRGB";
    case GL_SRGB8: return "GL_SRGB8";
    case GL_SRGB8_ALPHA8: return "GL_SRGB8_ALPHA8";
    case GL_COMPARE_REF_TO_TEXTURE: return "GL_COMPARE_REF_TO_TEXTURE";
    case GL_MAJOR_VERSION: return "GL_MAJOR_VERSION";
    case GL_MINOR_VERSION: return "GL_MINOR_VERSION";
    case GL_NUM_EXTENSIONS: return "GL_NUM_EXTENSIONS";
    case GL_RGBA32F: return "GL_RGBA32F";
    case GL_RGB32F: return "GL_RGB32F";
    case GL_RGBA16F: return "GL_RGBA16F";
    case GL_RGB16F: return "GL_RGB16F";
    case GL_VERTEX_ATTRIB_ARRAY_INTEGER: return "GL_VERTEX_ATTRIB_ARRAY_INTEGER";
    case GL_MAX_ARRAY_TEXTURE_LAYERS: return "GL_MAX_ARRAY_TEXTURE_LAYERS";
    case GL_MIN_PROGRAM_TEXEL_OFFSET: return "GL_MIN_PROGRAM_TEXEL_OFFSET";
    case GL_MAX_PROGRAM_TEXEL_OFFSET: return "GL_MAX_PROGRAM_TEXEL_OFFSET";
    case GL_MAX_VARYING_COMPONENTS: return "GL_MAX_VARYING_COMPONENTS";
    case GL_TEXTURE_2D_ARRAY: return "GL_TEXTURE_2D_ARRAY";
    case GL_TEXTURE_BINDING_2D_ARRAY: return "GL_TEXTURE_BINDING_2D_ARRAY";
    case GL_R11F_G11F_B10F: return "GL_R11F_G11F_B10F";
    case GL_UNSIGNED_INT_10F_11F_11F_REV: return "GL_UNSIGNED_INT_10F_11F_11F_REV";
    case GL_RGB9_E5: return "GL_RGB9_E5";
    case GL_UNSIGNED_INT_5_9_9_9_REV: return "GL_UNSIGNED_INT_5_9_9_9_REV";
    case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH:
        return "GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH";
    case GL_TRANSFORM_FEEDBACK_BUFFER_MODE: return "GL_TRANSFORM_FEEDBACK_BUFFER_MODE";
    case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS:
        return "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS";
    case GL_TRANSFORM_FEEDBACK_VARYINGS: return "GL_TRANSFORM_FEEDBACK_VARYINGS";
    case GL_TRANSFORM_FEEDBACK_BUFFER_START: return "GL_TRANSFORM_FEEDBACK_BUFFER_START";
    case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: return "GL_TRANSFORM_FEEDBACK_BUFFER_SIZE";
    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
        return "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN";
    case GL_RASTERIZER_DISCARD: return "GL_RASTERIZER_DISCARD";
    case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS:
        return "GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS";
    case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
        return "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS";
    case GL_INTERLEAVED_ATTRIBS: return "GL_INTERLEAVED_ATTRIBS";
    case GL_SEPARATE_ATTRIBS: return "GL_SEPARATE_ATTRIBS";
    case GL_TRANSFORM_FEEDBACK_BUFFER: return "GL_TRANSFORM_FEEDBACK_BUFFER";
    case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: return "GL_TRANSFORM_FEEDBACK_BUFFER_BINDING";
    case GL_RGBA32UI: return "GL_RGBA32UI";
    case GL_RGB32UI: return "GL_RGB32UI";
    case GL_RGBA16UI: return "GL_RGBA16UI";
    case GL_RGB16UI: return "GL_RGB16UI";
    case GL_RGBA8UI: return "GL_RGBA8UI";
    case GL_RGB8UI: return "GL_RGB8UI";
    case GL_RGBA32I: return "GL_RGBA32I";
    case GL_RGB32I: return "GL_RGB32I";
    case GL_RGBA16I: return "GL_RGBA16I";
    case GL_RGB16I: return "GL_RGB16I";
    case GL_RGBA8I: return "GL_RGBA8I";
    case GL_RGB8I: return "GL_RGB8I";
    case GL_RED_INTEGER: return "GL_RED_INTEGER";
    case GL_RGB_INTEGER: return "GL_RGB_INTEGER";
    case GL_RGBA_INTEGER: return "GL_RGBA_INTEGER";
    case GL_SAMPLER_2D_ARRAY: return "GL_SAMPLER_2D_ARRAY";
    case GL_SAMPLER_2D_ARRAY_SHADOW: return "GL_SAMPLER_2D_ARRAY_SHADOW";
    case GL_SAMPLER_CUBE_SHADOW: return "GL_SAMPLER_CUBE_SHADOW";
    case GL_UNSIGNED_INT_VEC2: return "GL_UNSIGNED_INT_VEC2";
    case GL_UNSIGNED_INT_VEC3: return "GL_UNSIGNED_INT_VEC3";
    case GL_UNSIGNED_INT_VEC4: return "GL_UNSIGNED_INT_VEC4";
    case GL_INT_SAMPLER_2D: return "GL_INT_SAMPLER_2D";
    case GL_INT_SAMPLER_3D: return "GL_INT_SAMPLER_3D";
    case GL_INT_SAMPLER_CUBE: return "GL_INT_SAMPLER_CUBE";
    case GL_INT_SAMPLER_2D_ARRAY: return "GL_INT_SAMPLER_2D_ARRAY";
    case GL_UNSIGNED_INT_SAMPLER_2D: return "GL_UNSIGNED_INT_SAMPLER_2D";
    case GL_UNSIGNED_INT_SAMPLER_3D: return "GL_UNSIGNED_INT_SAMPLER_3D";
    case GL_UNSIGNED_INT_SAMPLER_CUBE: return "GL_UNSIGNED_INT_SAMPLER_CUBE";
    case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return "GL_UNSIGNED_INT_SAMPLER_2D_ARRAY";
    case GL_BUFFER_ACCESS_FLAGS: return "GL_BUFFER_ACCESS_FLAGS";
    case GL_BUFFER_MAP_LENGTH: return "GL_BUFFER_MAP_LENGTH";
    case GL_BUFFER_MAP_OFFSET: return "GL_BUFFER_MAP_OFFSET";
    case GL_DEPTH_COMPONENT32F: return "GL_DEPTH_COMPONENT32F";
    case GL_DEPTH32F_STENCIL8: return "GL_DEPTH32F_STENCIL8";
    case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: return "GL_FLOAT_32_UNSIGNED_INT_24_8_REV";
    case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
        return "GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING";
    case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
        return "GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE";
    case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: return "GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE";
    case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: return "GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE";
    case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: return "GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE";
    case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: return "GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE";
    case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: return "GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE";
    case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: return "GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE";
    case GL_FRAMEBUFFER_DEFAULT: return "GL_FRAMEBUFFER_DEFAULT";
    case GL_FRAMEBUFFER_UNDEFINED: return "GL_FRAMEBUFFER_UNDEFINED";
    case GL_DEPTH_STENCIL_ATTACHMENT: return "GL_DEPTH_STENCIL_ATTACHMENT";
    case GL_DEPTH_STENCIL: return "GL_DEPTH_STENCIL";
    case GL_UNSIGNED_INT_24_8: return "GL_UNSIGNED_INT_24_8";
    case GL_DEPTH24_STENCIL8: return "GL_DEPTH24_STENCIL8";
    case GL_UNSIGNED_NORMALIZED: return "GL_UNSIGNED_NORMALIZED";
    case GL_READ_FRAMEBUFFER: return "GL_READ_FRAMEBUFFER";
    case GL_DRAW_FRAMEBUFFER: return "GL_DRAW_FRAMEBUFFER";
    case GL_READ_FRAMEBUFFER_BINDING: return "GL_READ_FRAMEBUFFER_BINDING";
    case GL_RENDERBUFFER_SAMPLES: return "GL_RENDERBUFFER_SAMPLES";
    case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: return "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER";
    case GL_MAX_COLOR_ATTACHMENTS: return "GL_MAX_COLOR_ATTACHMENTS";
    case GL_COLOR_ATTACHMENT1: return "GL_COLOR_ATTACHMENT1";
    case GL_COLOR_ATTACHMENT2: return "GL_COLOR_ATTACHMENT2";
    case GL_COLOR_ATTACHMENT3: return "GL_COLOR_ATTACHMENT3";
    case GL_COLOR_ATTACHMENT4: return "GL_COLOR_ATTACHMENT4";
    case GL_COLOR_ATTACHMENT5: return "GL_COLOR_ATTACHMENT5";
    case GL_COLOR_ATTACHMENT6: return "GL_COLOR_ATTACHMENT6";
    case GL_COLOR_ATTACHMENT7: return "GL_COLOR_ATTACHMENT7";
    case GL_COLOR_ATTACHMENT8: return "GL_COLOR_ATTACHMENT8";
    case GL_COLOR_ATTACHMENT9: return "GL_COLOR_ATTACHMENT9";
    case GL_COLOR_ATTACHMENT10: return "GL_COLOR_ATTACHMENT10";
    case GL_COLOR_ATTACHMENT11: return "GL_COLOR_ATTACHMENT11";
    case GL_COLOR_ATTACHMENT12: return "GL_COLOR_ATTACHMENT12";
    case GL_COLOR_ATTACHMENT13: return "GL_COLOR_ATTACHMENT13";
    case GL_COLOR_ATTACHMENT14: return "GL_COLOR_ATTACHMENT14";
    case GL_COLOR_ATTACHMENT15: return "GL_COLOR_ATTACHMENT15";
    case GL_COLOR_ATTACHMENT16: return "GL_COLOR_ATTACHMENT16";
    case GL_COLOR_ATTACHMENT17: return "GL_COLOR_ATTACHMENT17";
    case GL_COLOR_ATTACHMENT18: return "GL_COLOR_ATTACHMENT18";
    case GL_COLOR_ATTACHMENT19: return "GL_COLOR_ATTACHMENT19";
    case GL_COLOR_ATTACHMENT20: return "GL_COLOR_ATTACHMENT20";
    case GL_COLOR_ATTACHMENT21: return "GL_COLOR_ATTACHMENT21";
    case GL_COLOR_ATTACHMENT22: return "GL_COLOR_ATTACHMENT22";
    case GL_COLOR_ATTACHMENT23: return "GL_COLOR_ATTACHMENT23";
    case GL_COLOR_ATTACHMENT24: return "GL_COLOR_ATTACHMENT24";
    case GL_COLOR_ATTACHMENT25: return "GL_COLOR_ATTACHMENT25";
    case GL_COLOR_ATTACHMENT26: return "GL_COLOR_ATTACHMENT26";
    case GL_COLOR_ATTACHMENT27: return "GL_COLOR_ATTACHMENT27";
    case GL_COLOR_ATTACHMENT28: return "GL_COLOR_ATTACHMENT28";
    case GL_COLOR_ATTACHMENT29: return "GL_COLOR_ATTACHMENT29";
    case GL_COLOR_ATTACHMENT30: return "GL_COLOR_ATTACHMENT30";
    case GL_COLOR_ATTACHMENT31: return "GL_COLOR_ATTACHMENT31";
    case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
    case GL_MAX_SAMPLES: return "GL_MAX_SAMPLES";
    case GL_HALF_FLOAT: return "GL_HALF_FLOAT";
    case GL_RG: return "GL_RG";
    case GL_RG_INTEGER: return "GL_RG_INTEGER";
    case GL_R8: return "GL_R8";
    case GL_RG8: return "GL_RG8";
    case GL_R16F: return "GL_R16F";
    case GL_R32F: return "GL_R32F";
    case GL_RG16F: return "GL_RG16F";
    case GL_RG32F: return "GL_RG32F";
    case GL_R8I: return "GL_R8I";
    case GL_R8UI: return "GL_R8UI";
    case GL_R16I: return "GL_R16I";
    case GL_R16UI: return "GL_R16UI";
    case GL_R32I: return "GL_R32I";
    case GL_R32UI: return "GL_R32UI";
    case GL_RG8I: return "GL_RG8I";
    case GL_RG8UI: return "GL_RG8UI";
    case GL_RG16I: return "GL_RG16I";
    case GL_RG16UI: return "GL_RG16UI";
    case GL_RG32I: return "GL_RG32I";
    case GL_RG32UI: return "GL_RG32UI";
    case GL_VERTEX_ARRAY_BINDING: return "GL_VERTEX_ARRAY_BINDING";
    case GL_R8_SNORM: return "GL_R8_SNORM";
    case GL_RG8_SNORM: return "GL_RG8_SNORM";
    case GL_RGB8_SNORM: return "GL_RGB8_SNORM";
    case GL_RGBA8_SNORM: return "GL_RGBA8_SNORM";
    case GL_SIGNED_NORMALIZED: return "GL_SIGNED_NORMALIZED";
    case GL_PRIMITIVE_RESTART_FIXED_INDEX: return "GL_PRIMITIVE_RESTART_FIXED_INDEX";
    case GL_COPY_READ_BUFFER: return "GL_COPY_READ_BUFFER";
    case GL_COPY_WRITE_BUFFER: return "GL_COPY_WRITE_BUFFER";
    case GL_UNIFORM_BUFFER: return "GL_UNIFORM_BUFFER";
    case GL_UNIFORM_BUFFER_BINDING: return "GL_UNIFORM_BUFFER_BINDING";
    case GL_UNIFORM_BUFFER_START: return "GL_UNIFORM_BUFFER_START";
    case GL_UNIFORM_BUFFER_SIZE: return "GL_UNIFORM_BUFFER_SIZE";
    case GL_MAX_VERTEX_UNIFORM_BLOCKS: return "GL_MAX_VERTEX_UNIFORM_BLOCKS";
    case GL_MAX_FRAGMENT_UNIFORM_BLOCKS: return "GL_MAX_FRAGMENT_UNIFORM_BLOCKS";
    case GL_MAX_COMBINED_UNIFORM_BLOCKS: return "GL_MAX_COMBINED_UNIFORM_BLOCKS";
    case GL_MAX_UNIFORM_BUFFER_BINDINGS: return "GL_MAX_UNIFORM_BUFFER_BINDINGS";
    case GL_MAX_UNIFORM_BLOCK_SIZE: return "GL_MAX_UNIFORM_BLOCK_SIZE";
    case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS:
        return "GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS";
    case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS:
        return "GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS";
    case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: return "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT";
    case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: return "GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH";
    case GL_ACTIVE_UNIFORM_BLOCKS: return "GL_ACTIVE_UNIFORM_BLOCKS";
    case GL_UNIFORM_TYPE: return "GL_UNIFORM_TYPE";
    case GL_UNIFORM_SIZE: return "GL_UNIFORM_SIZE";
    case GL_UNIFORM_NAME_LENGTH: return "GL_UNIFORM_NAME_LENGTH";
    case GL_UNIFORM_BLOCK_INDEX: return "GL_UNIFORM_BLOCK_INDEX";
    case GL_UNIFORM_OFFSET: return "GL_UNIFORM_OFFSET";
    case GL_UNIFORM_ARRAY_STRIDE: return "GL_UNIFORM_ARRAY_STRIDE";
    case GL_UNIFORM_MATRIX_STRIDE: return "GL_UNIFORM_MATRIX_STRIDE";
    case GL_UNIFORM_IS_ROW_MAJOR: return "GL_UNIFORM_IS_ROW_MAJOR";
    case GL_UNIFORM_BLOCK_BINDING: return "GL_UNIFORM_BLOCK_BINDING";
    case GL_UNIFORM_BLOCK_DATA_SIZE: return "GL_UNIFORM_BLOCK_DATA_SIZE";
    case GL_UNIFORM_BLOCK_NAME_LENGTH: return "GL_UNIFORM_BLOCK_NAME_LENGTH";
    case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: return "GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS";
    case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: return "GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES";
    case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
        return "GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER";
    case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
        return "GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER";
    case GL_INVALID_INDEX: return "GL_INVALID_INDEX";
    case GL_MAX_VERTEX_OUTPUT_COMPONENTS: return "GL_MAX_VERTEX_OUTPUT_COMPONENTS";
    case GL_MAX_FRAGMENT_INPUT_COMPONENTS: return "GL_MAX_FRAGMENT_INPUT_COMPONENTS";
    case GL_MAX_SERVER_WAIT_TIMEOUT: return "GL_MAX_SERVER_WAIT_TIMEOUT";
    case GL_OBJECT_TYPE: return "GL_OBJECT_TYPE";
    case GL_SYNC_CONDITION: return "GL_SYNC_CONDITION";
    case GL_SYNC_STATUS: return "GL_SYNC_STATUS";
    case GL_SYNC_FLAGS: return "GL_SYNC_FLAGS";
    case GL_SYNC_FENCE: return "GL_SYNC_FENCE";
    case GL_SYNC_GPU_COMMANDS_COMPLETE: return "GL_SYNC_GPU_COMMANDS_COMPLETE";
    case GL_UNSIGNALED: return "GL_UNSIGNALED";
    case GL_SIGNALED: return "GL_SIGNALED";
    case GL_ALREADY_SIGNALED: return "GL_ALREADY_SIGNALED";
    case GL_TIMEOUT_EXPIRED: return "GL_TIMEOUT_EXPIRED";
    case GL_CONDITION_SATISFIED: return "GL_CONDITION_SATISFIED";
    case GL_WAIT_FAILED: return "GL_WAIT_FAILED";
    case GL_VERTEX_ATTRIB_ARRAY_DIVISOR: return "GL_VERTEX_ATTRIB_ARRAY_DIVISOR";
    case GL_ANY_SAMPLES_PASSED: return "GL_ANY_SAMPLES_PASSED";
    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: return "GL_ANY_SAMPLES_PASSED_CONSERVATIVE";
    case GL_SAMPLER_BINDING: return "GL_SAMPLER_BINDING";
    case GL_RGB10_A2UI: return "GL_RGB10_A2UI";
    case GL_TEXTURE_SWIZZLE_R: return "GL_TEXTURE_SWIZZLE_R";
    case GL_TEXTURE_SWIZZLE_G: return "GL_TEXTURE_SWIZZLE_G";
    case GL_TEXTURE_SWIZZLE_B: return "GL_TEXTURE_SWIZZLE_B";
    case GL_TEXTURE_SWIZZLE_A: return "GL_TEXTURE_SWIZZLE_A";
    case GL_GREEN: return "GL_GREEN";
    case GL_BLUE: return "GL_BLUE";
    case GL_INT_2_10_10_10_REV: return "GL_INT_2_10_10_10_REV";
    case GL_TRANSFORM_FEEDBACK: return "GL_TRANSFORM_FEEDBACK";
    case GL_TRANSFORM_FEEDBACK_PAUSED: return "GL_TRANSFORM_FEEDBACK_PAUSED";
    case GL_TRANSFORM_FEEDBACK_ACTIVE: return "GL_TRANSFORM_FEEDBACK_ACTIVE";
    case GL_TRANSFORM_FEEDBACK_BINDING: return "GL_TRANSFORM_FEEDBACK_BINDING";
    case GL_PROGRAM_BINARY_RETRIEVABLE_HINT: return "GL_PROGRAM_BINARY_RETRIEVABLE_HINT";
    case GL_PROGRAM_BINARY_LENGTH: return "GL_PROGRAM_BINARY_LENGTH";
    case GL_NUM_PROGRAM_BINARY_FORMATS: return "GL_NUM_PROGRAM_BINARY_FORMATS";
    case GL_PROGRAM_BINARY_FORMATS: return "GL_PROGRAM_BINARY_FORMATS";
    case GL_COMPRESSED_R11_EAC: return "GL_COMPRESSED_R11_EAC";
    case GL_COMPRESSED_SIGNED_R11_EAC: return "GL_COMPRESSED_SIGNED_R11_EAC";
    case GL_COMPRESSED_RG11_EAC: return "GL_COMPRESSED_RG11_EAC";
    case GL_COMPRESSED_SIGNED_RG11_EAC: return "GL_COMPRESSED_SIGNED_RG11_EAC";
    case GL_COMPRESSED_RGB8_ETC2: return "GL_COMPRESSED_RGB8_ETC2";
    case GL_COMPRESSED_SRGB8_ETC2: return "GL_COMPRESSED_SRGB8_ETC2";
    case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
        return "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2";
    case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
        return "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2";
    case GL_COMPRESSED_RGBA8_ETC2_EAC: return "GL_COMPRESSED_RGBA8_ETC2_EAC";
    case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC";
    case GL_TEXTURE_IMMUTABLE_FORMAT: return "GL_TEXTURE_IMMUTABLE_FORMAT";
    case GL_MAX_ELEMENT_INDEX: return "GL_MAX_ELEMENT_INDEX";
    case GL_NUM_SAMPLE_COUNTS: return "GL_NUM_SAMPLE_COUNTS";
    case GL_TEXTURE_IMMUTABLE_LEVELS: return "GL_TEXTURE_IMMUTABLE_LEVELS";
    default: return "Unknown GL Enum"; break;
    }
    return "Unknown GL Enum";
}

GLState& GLState::get()
{
    if (m_instance == nullptr) m_instance = std::make_unique<GLState>();
    return *m_instance;
}


GLState::GLState()
    : m_stack_level(1)
    , m_gl_thread_id(std::this_thread::get_id())
{
    get_stack(glDepthMask).push([]() { glDepthMask(GL_TRUE); });
    get_stack(glDepthFunc).push([]() { glDepthFunc(GL_LESS); });

    get_stack(glBlendFunc).push([]() { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); });
    get_stack(glBlendEquation).push([]() { glBlendEquation(GL_FUNC_ADD); });
    get_stack(glClearColor).push([]() { glClearColor(0, 0, 0, 0); });
    get_stack(glViewport).push([]() { /*No default*/ });
    get_stack(glCullFace).push([]() { glCullFace(GL_BACK); });
    get_stack(glFrontFace).push([]() { glFrontFace(GL_CCW); });
#if !defined(__EMSCRIPTEN__)
    // TODO WebGL: glPolygonMode is not supported.
    get_stack(glPolygonMode).push([]() { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); });
#endif
    get_stack(glColorMask).push([]() { glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); });

    set_toggle(true, GL_DEPTH_TEST);
    set_toggle(true, GL_BLEND);
    set_toggle(true, GL_CULL_FACE);
}

void GLState::pop()
{
    auto& state = get();
    if (state.m_stack_level == 0) {
        assert(false);
    }

    for (auto& it : state.values) {
        auto& stack = it.second;
        // Copy non-empty stack top value
        if (stack.size() > 1) {
            stack.pop();
            auto& fn = stack.top();
            fn();
        } else if (it.second.size() == 1) {
            auto& fn = stack.top();
            fn();
        }
    }

    for (auto& it : state.toggles) {
        auto& stack = it.second;
        if (stack.size() > 1) {
            stack.pop();
            bool value = stack.top();
            if (value)
                glEnable(it.first);
            else
                glDisable(it.first);
        } else if (stack.size() == 1) {
            bool value = stack.top();
            if (value)
                glEnable(it.first);
            else
                glDisable(it.first);
        }
    }

    state.m_stack_level--;
}

void GLState::push()
{
    auto& state = get();
    for (auto& it : state.values) {
        // Copy non-empty stack top value
        if (it.second.size() > 0) it.second.push(it.second.top());
    }

    for (auto& it : state.toggles) {
        // Copy non-empty stack top value
        if (it.second.size() > 0) it.second.push(it.second.top());
    }
    state.m_stack_level++;
}

GLScope::GLScope(bool push)
    : m_push(push)
{
    if (m_push) GLState::push();
}

GLScope::~GLScope()
{
    if (m_push) GLState::pop();
}

bool checkGLError(const char* label)
{
    bool hasErr = false;
    GLenum err;
    while ((err = glGetError()) != GL_NO_ERROR) {
        switch (err) {
        case GL_INVALID_ENUM: logger().error("GL_INVALID_ENUM: {}", label); break;
        case GL_INVALID_VALUE: logger().error("GL_INVALID_VALUE: {}", label); break;
        case GL_INVALID_OPERATION: logger().error("GL_INVALID_OPERATION: {}", label); break;
        case GL_STACK_OVERFLOW: logger().error("GL_STACK_OVERFLOW: {}", label); break;
        case GL_STACK_UNDERFLOW: logger().error("GL_STACK_UNDERFLOW: {}", label); break;
        case GL_OUT_OF_MEMORY: logger().error("GL_OUT_OF_MEMORY: {}", label); break;
        case GL_INVALID_FRAMEBUFFER_OPERATION:
            logger().error("GL_INVALID_FRAMEBUFFER_OPERATION: {}", label);
            break;
        default: logger().error("Unknown Error: {}", label); break;
        }
        hasErr = true;
        break;
    }

    return hasErr;
}

} // namespace ui
} // namespace lagrange
